package org.ring.common.transport;

import io.netty.buffer.ByteBuf;

import java.io.IOException;

/**
 * @since 2023-10-27 20:21
 */
public class MessageCodec implements Codec<Message> {

    //len + msgId + flag (4 + 8 + 4)
    private static final int HEADER_LEN = 16;

    protected Hessian2Serialization serialization = new Hessian2Serialization();

    @Override
    public void encode(Message message, ByteBuf buf) throws IOException {

        int savedWriterIndex = buf.writerIndex();
        buf.writerIndex(savedWriterIndex + HEADER_LEN); // reserve len space

        int writtenBytes = encodeData(message, buf);

        int len = HEADER_LEN + writtenBytes;
        buf.writerIndex(savedWriterIndex);
        buf.writeInt(len);
        buf.writeInt(message.getFlag());
        buf.writeLong(message.getId());

        buf.writerIndex(savedWriterIndex + HEADER_LEN + len);
    }

    @Override
    public Message decode(ByteBuf buf) throws IOException {
        int n = buf.readableBytes();
        if (n < HEADER_LEN) {
            return null;
        }
        int len = buf.readInt();
        if (n < len) {
            return null;
        }
        int flag = buf.readInt();
        long msgId = buf.readLong();

        int payloadLen = len - HEADER_LEN;
        Message message = new Message();
        message.setLen(len);
        message.setFlag(flag);
        message.setId(msgId);
        if(payloadLen <= 0){
            return message;
        }
        Object data = null;
        if(message.isRequest()){
            data = decodeRequestData(buf,payloadLen);
        } else {
            data = decodeData(buf, payloadLen);
        }
        message.setData(data);

        return message;
    }

    protected int encodeData(Message message, ByteBuf buf) throws IOException {
        // TODO encode exception handler
        Object data = message.getData();
        ByteBufOutputStream output = new ByteBufOutputStream(buf);
        serialization.serialize(data, output);
        return output.writtenBytes();
    }

    protected Object decodeData(ByteBuf buf, int payloadLen) throws IOException {
        //decode exception handler , should discard frame buf
        ByteBufInputStream in = new ByteBufInputStream(buf);
        return serialization.deserialize(in);
    }

    protected Object decodeRequestData(ByteBuf buf, int payloadLen) throws IOException {
        ByteBufInputStream in = new ByteBufInputStream(buf);
        return serialization.deserialize(in);
    }
}
